//
// Created by 朕与将军解战袍 on 2022/3/12.
//

// std
#include <math.h>
#include <stdlib.h>

// TODO 删除
#define TTF_RENDER_IMPLEMENT

#include "raster.h"

#ifdef TTF_RENDER_IMPLEMENT

TTFStatus seedFillGlyph(
        OUT unsigned char *bitmap,
        IN int w,
        IN int h,
        IN Glyph *glyph,
        IN int size) {
    TTFStatus status = SUCCESS;
    // 生成边界
    DiagonalLine diagonalLine;
    status = generateBoundary(&diagonalLine, bitmap, w, h, glyph, size);
    if (TTF_ERR(status)) {
        return status;
    }
    // 生成种子像素
    Vertex seed;
    status = searchSeed(&seed, &diagonalLine, bitmap, w);
    if (TTF_ERR(status)) {
        return status;
    }
    // 初始化栈
    int cap = ceil(sqrt(w*w + h*h));
    Vertex *stack = ALLOCATE(cap * sizeof(Vertex));
    int top = 1;
    // 种子进栈
    stack[0] = seed;
    while (top > 0) {
        int x = (int)stack[top - 1].x;
        int y = (int)stack[top - 1].y;
        top--; // 出栈
        // 向上走到边界
        while (bitmap[(y - 1) * w + x] == EMPTY) {
            y--;
        }
        int reachLeft = 0;
        int reachRight = 0;
        while (bitmap[y * w + x] == EMPTY) {
            // 填充像素
            bitmap[y * w + x] = FILLED;
            // 判断左侧像素是否已填充
            if (!reachLeft && bitmap[y * w + x - 1] == EMPTY) {
                // 未填充, 将其进栈
                stack[top].x = x - 1;
                stack[top].y = y;
                top++;
                reachLeft = 1;
            }
            if (bitmap[y * w + x - 1] == FILLED) {
                reachLeft = 0;
            }
            // 判断右侧像素是否已填充
            if (!reachRight && bitmap[y * w + x + 1] == EMPTY) {
                // 未填充, 将其进栈
                stack[top].x = x + 1;
                stack[top].y = y;
                top++;
                reachRight = 1;
            }
            if (bitmap[y * w + x + 1] == FILLED) {
                reachRight = 0;
            }
            y++;
        }
    }
    DEALLOCATE(stack);
    return status;
}

TTFStatus generateBoundary(
        OUT DiagonalLine* diagonalLine,
        OUT unsigned char *bitmap,
        IN int w,
        IN int h,
        IN Glyph *glyph,
        IN int size) {
    TTFStatus status = SUCCESS;
    for (int i = 0; i < size; i++) {
        switch (glyph[i].type) {
            case POLYGON:
                status = SUCCESS;
                break;
            case BEZIER_QUADRATIC:
                status = generateBezierQuadraticBoundary(
                        diagonalLine,
                        bitmap, w, h,
                        glyph[i].data, glyph[i].size);
                break;
            default:
                return INVALID_PARAMETER;
        }
        if (TTF_ERR(status)) {
            return status;
        }
    }
    return status;
}

// Px(t) = t^2 * (P0.x - 2P1.x + P2.x) + t * (2P1.x - P0.x) + P0.x --- 3
// Py(t) = t^2 * (P0.y - 2P1.y + P2.y) + t * (2P1.y - P0.y) + P0.y --- 4
//               |-------- a --------|       |----- b ----|
TTFStatus generateBezierQuadraticBoundary(
        OUT DiagonalLine* diagonalLine,
        OUT unsigned char *bitmap,
        IN int w,
        IN int h,
        IN BezierQuadratic* curve,
        IN int size) {
    diagonalLine->topLeft.x = w + 1;
    diagonalLine->topLeft.y = h + 1;
    diagonalLine->downRight.x = -1;
    diagonalLine->downRight.y = -1;

    for (int i = 0; i < size; i++) {
        bitmap[(int)curve[i].p0.y * w + (int)curve[i].p2.x] = FILLED;
        bitmap[(int)curve[i].p2.y * w + (int)curve[i].p2.x] = FILLED;

        if (curve[i].p0.x < diagonalLine->topLeft.x
         && curve[i].p0.y < diagonalLine->topLeft.y) {
            diagonalLine->topLeft.x = curve[i].p0.x;
            diagonalLine->topLeft.y = curve[i].p0.y;
        }
        if (curve[i].p0.x > diagonalLine->downRight.x
         && curve[i].p0.y > diagonalLine->downRight.y) {
            diagonalLine->downRight.x = curve[i].p0.x;
            diagonalLine->downRight.y = curve[i].p0.y;
        }

        if (curve[i].p2.x < diagonalLine->topLeft.x
         && curve[i].p2.y < diagonalLine->topLeft.y) {
            diagonalLine->topLeft.x = curve[i].p2.x;
            diagonalLine->topLeft.y = curve[i].p2.y;
        }
        if (curve[i].p2.x > diagonalLine->downRight.x
         && curve[i].p2.y > diagonalLine->downRight.y) {
            diagonalLine->downRight.x = curve[i].p2.x;
            diagonalLine->downRight.y = curve[i].p2.y;
        }

        double t = 0.0;
        double p1_2x = 2 * curve[i].p1.x;
        double p1_2y = 2 * curve[i].p1.y;
        double ax = curve[i].p0.x - p1_2x + curve[i].p2.x;
        double bx = p1_2x - curve[i].p0.x;
        double ay = curve[i].p0.y - p1_2y + curve[i].p2.y;
        double by = p1_2y - curve[i].p0.y;
        while (t < 1.0) {
            double t2 = t * t;
            double x = t2 * ax + t * bx + curve[i].p0.x;
            double y = t2 * ay + t * by + curve[i].p0.y;
            bitmap[(int)y * w + (int)x] = FILLED;

            t += 1.f / (float)(w + h);
        }
    }

    return SUCCESS;
}

TTFStatus searchSeed(
        OUT Vertex *seed,
        IN DiagonalLine *diagonalLine,
        IN const unsigned char *bitmap,
        IN int w) {

    Vertex first, second;
    // 寻找种子
    // p0 p2
    // o  o  --- y0
    //  \
    //   \
    // o  o  --- y1
    // x0 x1
    // p3 p1
    // p2 = (x1, y0)
    // p3 = (x0, y1)
    double x0, y0, x1, y1, dy;
    int cnt = 0;
    x0 = diagonalLine->topLeft.x;
    y0 = diagonalLine->topLeft.y;
    x1 = diagonalLine->downRight.x;
    y1 = diagonalLine->downRight.y;
    dy = (y0 - y1) / (x1 - x0);
    // 从 p3 搜索到 p2
    while (x0 < x1) {
        if (bitmap[(int)y1 * w + (int)x0] == FILLED) {
            if (cnt == 0) {
                first.x = x0;
                first.y = y1;
            } else if (cnt == 1) {
                second.x = x0;
                second.y = y0;
                cnt++;
                break;
            }
            cnt += 1;
        }
        x0 += 1;
        y1 += dy;
    }
    if (cnt <= 1) {
        return CAN_NOT_FOUND_SEED;
    }

    seed->x = ((int)first.x + (int)second.x) / 2.0;
    seed->y = ((int)first.y + (int)second.y) / 2.0;

    return SUCCESS;
}

#endif